/*
 * 本程序提取自cups-1.3.9
 * 可能检测打印机型号
 * 作者：kangear
 * 联系方式:　kangear@163.com
 * 时间：２０１４-０２-１１
 * 输入样例：
 * 
 * direct usb://Canon/ "" " USB #1" "MFG:Canon;CMD:BJL,BJRaster3,BSCCe,NCCe,IVEC,IVECPLI;
 * SOJ:BJNP2,BJNPe;MDL:MX420 series;CLS:PRINTER;DES:Canon MX420 series;VER:1.020;STA:10;
 * FSI:04;HRI:CHN;MSI:DAT,E3,HASF;PDR:7;PSE:LFBA05000;"
 * 
 */

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <ctype.h>

//#define DEBUG 0

#  ifdef DEBUG
#    define DEBUG_puts(x) puts(x)
#    define DEBUG_printf(x) printf x
#  else
#    define DEBUG_puts(x)
#    define DEBUG_printf(x)
#  endif /* DEBUG */

#  define IOCNR_GET_DEVICE_ID		1
#  define LPIOC_GET_DEVICE_ID(len)	_IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)


size_t strlcpy(char *, const char *, size_t);

/*
 * Copy src to string dst of size siz.  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz == 0).
 * Returns strlen(src); if retval >= siz, truncation occurred.
 */
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
        char *d = dst;
        const char *s = src;
        size_t n = siz;

        /* Copy as many bytes as will fit */
        if (n != 0 && --n != 0) {
                do {
                        if ((*d++ = *s++) == 0)
                                break;
                } while (--n != 0);
        }

        /* Not enough room in dst, add NUL and traverse rest of src */
        if (n == 0) {
                if (siz != 0)
                        *d = '\0';                /* NUL-terminate dst */
                while (*s++)
                        ;
        }

        return(s - src - 1);        /* count does not include NUL */
}

size_t  strlcat(char *, const char *, size_t);

/*
 * Appends src to string dst of size siz (unlike strncat, siz is the
 * full size of dst, not space left).  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
 * If retval >= siz, truncation occurred.
 */
size_t
strlcat(char *dst, const char *src, size_t siz)
{
        char *d = dst;
        const char *s = src;
        size_t n = siz;
        size_t dlen;

        /* Find the end of dst and adjust bytes left but don't go past end */
        while (n-- != 0 && *d != '\0')
                d++;
        dlen = d - dst;
        n = siz - dlen;

        if (n == 0)
                return(dlen + strlen(s));
        while (*s != '\0') {
                if (n != 1) {
                        *d++ = *s;
                        n--;
                }
                s++;
        }
        *d = '\0';

        return(dlen + (s - src));        /* count does not include NUL */
}

/*
 * 'backendGetDeviceID()' - Get the IEEE-1284 device ID string and
 *                          corresponding URI.
 */

int					/* O - 0 on success, -1 on failure */
backendGetDeviceID(
    int        fd,			/* I - File descriptor */
    char       *device_id,		/* O - 1284 device ID */
    int        device_id_size,		/* I - Size of buffer */
    char       *make_model,		/* O - Make/model */
    int        make_model_size,		/* I - Size of buffer */
    const char *scheme,			/* I - URI scheme */
    char       *uri,			/* O - Device URI */
    int        uri_size)		/* I - Size of buffer */
{
#ifdef __APPLE__ /* This function is a no-op */
  return (-1);

#else /* Get the device ID from the specified file descriptor... */
  char	*attr,				/* 1284 attribute */
  	*delim,				/* 1284 delimiter */
	*uriptr,			/* Pointer into URI */
	manufacturer[256],		/* Manufacturer string */
	serial_number[1024];		/* Serial number string */
  int	manulen;			/* Length of manufacturer string */
#  ifdef __linux
  int	length;				/* Length of device ID info */
  int   got_id = 0;
#  endif /* __linux */
#  if defined(__sun) && defined(ECPPIOC_GETDEVID)
  struct ecpp_device_id did;		/* Device ID buffer */
#  endif /* __sun && ECPPIOC_GETDEVID */


  DEBUG_printf(("backendGetDeviceID(fd=%d, device_id=%p, device_id_size=%d, "
                "make_model=%p, make_model_size=%d, scheme=\"%s\", "
		"uri=%p, uri_size=%d)\n", fd, device_id, device_id_size,
		make_model, make_model_size, scheme ? scheme : "(null)",
		uri, uri_size));

 /*
  * Range check input...
  */

  if (!device_id || device_id_size < 32)
  {
    DEBUG_puts("backendGetDeviceID: Bad args!");
    return (-1);
  }

  if (make_model)
    *make_model = '\0';

  if (fd >= 0)
  {
   /*
    * Get the device ID string...
    */

    *device_id = '\0';

#  ifdef __linux
  if (ioctl(fd, LPIOC_GET_DEVICE_ID(device_id_size), device_id))
  {
     printf("ERROR!");
     return 0;
  }
  else
    got_id = 1;

  if (got_id)
    {
     /*
      * Extract the length of the device ID string from the first two
      * bytes.  The 1284 spec says the length is stored MSB first...
      */

      length = (((unsigned)device_id[0] & 255) << 8) +
	       ((unsigned)device_id[1] & 255);

     /*
      * Check to see if the length is larger than our buffer; first
      * assume that the vendor incorrectly implemented the 1284 spec,
      * and then limit the length to the size of our buffer...
      */

      if (length > (device_id_size - 2))
	length = (((unsigned)device_id[1] & 255) << 8) +
		 ((unsigned)device_id[0] & 255);

      if (length > (device_id_size - 2))
	length = device_id_size - 2;

     /*
      * Copy the device ID text to the beginning of the buffer and
      * nul-terminate.
      */

      memmove(device_id, device_id + 2, length);
      device_id[length] = '\0';
    }
#    ifdef DEBUG
    else
      printf("backendGetDeviceID: ioctl failed - %s\n", strerror(errno));
#    endif /* DEBUG */
#  endif /* __linux */

#   if defined(__sun) && defined(ECPPIOC_GETDEVID)
    did.mode = ECPP_CENTRONICS;
    did.len  = device_id_size - 1;
    did.rlen = 0;
    did.addr = device_id;

    if (!ioctl(fd, ECPPIOC_GETDEVID, &did))
    {
     /*
      * Nul-terminate the device ID text.
      */

      if (did.rlen < (device_id_size - 1))
	device_id[did.rlen] = '\0';
      else
	device_id[device_id_size - 1] = '\0';
    }
#    ifdef DEBUG
    else
      printf("backendGetDeviceID: ioctl failed - %s\n", strerror(errno));
#    endif /* DEBUG */
#  endif /* __sun && ECPPIOC_GETDEVID */
  }

  DEBUG_printf(("backendGetDeviceID: device_id=\"%s\"\n", device_id));

  if (scheme && uri)
    *uri = '\0';

  if (!*device_id)
    return (-1);

 /*
  * Get the make and model...
  */

  //if (make_model)
  //  backendGetMakeModel(device_id, make_model, make_model_size);

 /*
  * Then generate a device URI...
  */

  if (scheme && uri && uri_size > 32)
  {
   /*
    * Look for the serial number field...
    */

    if ((attr = strstr(device_id, "SERN:")) != NULL)
      attr += 5;
    else if ((attr = strstr(device_id, "SERIALNUMBER:")) != NULL)
      attr += 13;
    else if ((attr = strstr(device_id, ";SN:")) != NULL)
      attr += 4;

    if (attr)
    {
      strlcpy(serial_number, attr, sizeof(serial_number));

      if ((delim = strchr(serial_number, ';')) != NULL)
	*delim = '\0';
    }
    else
      serial_number[0] = '\0';

   /*
    * Generate the device URI from the manufacturer, make_model, and
    * serial number strings.
    */

    snprintf(uri, uri_size, "%s://", scheme);

    if ((attr = strstr(device_id, "MANUFACTURER:")) != NULL)
      attr += 13;
    else if ((attr = strstr(device_id, "Manufacturer:")) != NULL)
      attr += 13;
    else if ((attr = strstr(device_id, "MFG:")) != NULL)
      attr += 4;

    if (attr)
    {
      strlcpy(manufacturer, attr, sizeof(manufacturer));

      if ((delim = strchr(manufacturer, ';')) != NULL)
        *delim = '\0';

      if (!strcasecmp(manufacturer, "Hewlett-Packard"))
        strcpy(manufacturer, "HP");
      else if (!strcasecmp(manufacturer, "Lexmark International"))
        strcpy(manufacturer, "Lexmark");
    }
    else
    {
      strlcpy(manufacturer, make_model, sizeof(manufacturer));

      if ((delim = strchr(manufacturer, ' ')) != NULL)
        *delim = '\0';
    }

    manulen = strlen(manufacturer);

    for (uriptr = uri + strlen(uri), delim = manufacturer;
	 *delim && uriptr < (uri + uri_size - 3);
	 delim ++)
      if (*delim == ' ')
      {
	*uriptr++ = '%';
	*uriptr++ = '2';
	*uriptr++ = '0';
      }
      else
	*uriptr++ = *delim;

    *uriptr++ = '/';

    if (!strncasecmp(make_model, manufacturer, manulen))
    {
      delim = make_model + manulen;

      while (isspace(*delim & 255))
        delim ++;
    }
    else
      delim = make_model;

    for (; *delim && uriptr < (uri + uri_size - 3); delim ++)
      if (*delim == ' ')
      {
	*uriptr++ = '%';
	*uriptr++ = '2';
	*uriptr++ = '0';
      }
      else
	*uriptr++ = *delim;

    if (serial_number[0])
    {
     /*
      * Add the serial number to the URI...
      */

      strlcpy(uriptr, "?serial=", uri_size - (uriptr - uri));
      strlcat(uriptr, serial_number, uri_size - (uriptr - uri));
    }
    else
      *uriptr = '\0';
  }

  return (0);
#endif /* __APPLE__ */
}

/*
 * 'list_devices()' - List all USB devices.
 */

void
list_devices(void)
{
  int	i;				/* Looping var */
  int	fd;				/* File descriptor */
  char	device[255],			/* Device filename */
	device_id[1024],		/* Device ID string */
	device_uri[1024],		/* Device URI string */
	make_model[1024];		/* Make and model */

 /*
  * Try to open each USB device...
  */

  for (i = 0; i < 16; i ++)
  {
   /*
    * Linux has a long history of changing the standard filenames used
    * for USB printer devices.  We get the honor of trying them all...
    */

    sprintf(device, "/dev/usblp%d", i);

    if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
    {
      if (errno != ENOENT)
	continue;

      sprintf(device, "/dev/usb/lp%d", i);

      if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
      {
	if (errno != ENOENT)
	  continue;

	sprintf(device, "/dev/usb/usblp%d", i);

    	if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
	  continue;
      }
    }

    if (!backendGetDeviceID(fd, device_id, sizeof(device_id),
                            make_model, sizeof(make_model),
			    "usb", device_uri, sizeof(device_uri)))
      printf("direct %s \"%s\" \"%s USB #%d\" \"%s\"\n", device_uri,
	     make_model, make_model, i + 1, device_id);

    close(fd);
  }
}
  
int main()
{
	list_devices();
	return 0;
}
